/* 
 *  * tcpclient.c - A simple TCP client
 *   * usage: tcpclient <host> <port>
 *    */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <errno.h>

#define BUFSIZE 1024

typedef struct jsondata{
	union {
		char buffer[2];
		int nSize;
	} sizeofdata;
	int nBytesRead;
	char* pData;
} JSONDATA;
/* 
 *  * error - wrapper for perror
 *   */
void error(char *msg) {
    perror(msg);
    exit(0);
}

static int
read_size_of_receivable_data(int fd, JSONDATA* pJsonData) {
	ssize_t received = 0;

	while(pJsonData->nBytesRead != 2) {
		received = read(fd, pJsonData->sizeofdata.buffer, 2-pJsonData->nBytesRead);
		if (received == -1) {
			/* If errno == EAGAIN, that means we have read all
			data. So go back to the main loop. */
			if (errno != EAGAIN) {
				perror ("read");
				return -1;
			}
			break;
		} else if (received == 0) {
			/* End of file. The remote has closed the
			connection. */
			return -1;
		} else {
			pJsonData->nBytesRead += received;
		}
	}

	if(pJsonData->nBytesRead > 2) {
		printf("Something went wrong while reading\n");
		return -1;
	} else if(pJsonData->nBytesRead < 2) {
		return 0;
	}

	return 2;
}

static int
read_receivable_data(int fd, JSONDATA* pJsonData) {
	if(pJsonData == NULL) {

		return -1;
	}

	while (1) {
		ssize_t count = 0;
		char buf[512];
		count = read (fd, buf, sizeof(buf));
		if (count == -1) {
			/* If errno == EAGAIN, that means we have read all
			   data. So go back to the main loop. */
			if (errno != EAGAIN) {
				perror ("read");
				return -1;
			} else {
				return 0;
			}
		} else if (count == 0) {
			/* End of file. The remote has closed the
			   connection. */
			return -1;
		} else if(count > 0) {
			memcpy(pJsonData->pData + (pJsonData->nBytesRead - 2), buf, count);
			pJsonData->nBytesRead += count;
			if((pJsonData->nBytesRead - 2) == pJsonData->sizeofdata.nSize) {
				return pJsonData->nBytesRead - 2;
			} 
		}
	}

	return 0;
}
int read_file_contents(char* pFilename, char** pBuffer) {
	char *source = NULL;
	long bufsize=0;
	FILE *fp = fopen(pFilename, "r");
	if (fp != NULL) {
	/* Go to the end of the file. */
		if (fseek(fp, 0L, SEEK_END) == 0) {
			/* Get the size of the file. */
			bufsize = ftell(fp);
			if (bufsize == -1) {
			 /* Error */
				return -1; 
			}
			
			/* Allocate our buffer to that size. */
			source = (char*)malloc(sizeof(char) * (bufsize + 1));
			
			/* Go back to the start of the file. */
			if (fseek(fp, 0L, SEEK_SET) == 0) { /* Error */ }
			
			/* Read the entire file into memory. */
			size_t newLen = fread(source, sizeof(char), bufsize, fp);
			if (newLen == 0) {
			    fputs("Error reading file", stderr);
			} else {
			    source[++newLen] = '\0'; /* Just to be safe. */
			}
		}
		fclose(fp);
	}
	*pBuffer = source;

	return bufsize + 1 ;
}



int main(int argc, char **argv) {
    int sockfd, portno, n;
    struct sockaddr_in serveraddr;
    struct hostent *server;
    char *hostname;
//    char buf[BUFSIZE];

    /* check command line arguments */
    if (argc != 4) {
       fprintf(stderr,"usage: %s <hostname> <port>\n", argv[0]);
       exit(0);
    }
    hostname = argv[1];
    portno = atoi(argv[2]);

    /* socket: create the socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");

    /* gethostbyname: get the server's DNS entry */
    server = gethostbyname(hostname);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host as %s\n", hostname);
        exit(0);
    }

    /* build the server's Internet address */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
	  (char *)&serveraddr.sin_addr.s_addr, server->h_length);
    serveraddr.sin_port = htons(portno);

    /* connect: create a connection with the server */
    if (connect(sockfd, (sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 
      error("ERROR connecting");

    /* get message line from the user */
//    printf("Please enter msg: ");
 //   bzero(buf, BUFSIZE);
  //  fgets(buf, BUFSIZE, stdin);


/*	union {
		short nSize;
		char buffer[2];
	} size_of_data;
	size_of_data.nSize = strlen(buf); */
	char* buf = NULL;
	short sizeOfData = read_file_contents(argv[3], &buf);
	printf("File Contents: %s\n", buf);
	n = write(sockfd, &sizeOfData, sizeof(short));

    /* send the message line to the server */
    n = write(sockfd, buf, sizeOfData);
    if (n < 0) 
      error("ERROR writing to socket");

	free(buf); buf = NULL;


	JSONDATA* pJsonData = NULL;
	pJsonData = (JSONDATA*) malloc(sizeof(JSONDATA));
	memset(pJsonData, 0, sizeof(JSONDATA));

	if(pJsonData->nBytesRead < 2) {
		int size_of_data = read_size_of_receivable_data(sockfd, pJsonData);
		if(size_of_data < 0) {
			close (sockfd);
			printf("Failed to read size of receivable data\n");
			return 1;
		} else if(size_of_data == 0) {
			close (sockfd);
			printf("server closed the connection\n");
			return 1;
		}
	}

	printf("size of receivable data is %d\n", pJsonData->sizeofdata.nSize);
	pJsonData->pData = (char*) malloc(pJsonData->sizeofdata.nSize + 1);
	int nBytesRead = read_receivable_data(sockfd, pJsonData);

	if(nBytesRead >= 0) {
			pJsonData->nBytesRead += nBytesRead;
			if(pJsonData->nBytesRead - 2 >= pJsonData->sizeofdata.nSize) {
				printf("Respose received is %s\n", pJsonData->pData); 
			}
	}

    close(sockfd);
    return 0;
}

